﻿using System;
using System.Collections;
using System.Net;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.Json;

namespace Kugar.Core.ExtMethod
{
    public static class JSONExt
    {
        private const string datetimeReg = @"\b(?<=\/Date\()((\d+)(\++)(\d+))(?=\))\b";
        private static Regex jsonDateRegex = new Regex(datetimeReg);

        public static bool IsJsonDateTime(this JsonValue jsonValue)
        {
            if (jsonDateRegex.IsMatch(jsonValue.ToString()))
            {
                return true;
            }
            else
            {
                return false;
            }


        }

        public static DateTime JsonStringToDateTime(this JsonValue jsonValue)
        {
            if (jsonDateRegex.IsMatch(jsonValue.ToString()))
            {
                return JsonToDateTime(jsonDateRegex.Match(jsonValue).Groups[0].Value);
            }
            else
            {
                return default(DateTime);
            }
        }

        public static object FormatJsonValue(this JsonValue jsonValue)
        {
            return FormatJsonValue(jsonValue, false, null);
        }

        public static object FormatJsonValue(this JsonValue jsonValue,bool isReturnDefaultValue,Type targetType)
        {

            object value = null;

            try
            {
                if (jsonValue == null)
                {
                    if (isReturnDefaultValue && targetType!=null)
                    {
                        value = targetType.IsValueType ? Activator.CreateInstance(targetType) : null;
                    }
                    else
                    {
                        value =null;
                    }
                    
                }
                else if (IsJsonDateTime(jsonValue))
                {
                    value = JsonToDateTime(jsonDateRegex.Match(jsonValue.ToString()).Groups[0].Value);
                }
                else if (jsonValue.JsonType == JsonType.String)
                {
                    value = jsonValue.ToString().Trim('\"');
                }
                else
                {
                    value = jsonValue;
                }

            }
            catch (Exception)
            {
                return jsonValue;
            }

            return value;
        }

        public static Dictionary<string, object> ToSingleDictionary(this JsonObject json)
        {

            var dict = new Dictionary<string, object>();
            foreach (var key in json.Keys)
            {
                //object value = null;
                var jsonValue = json[key];
                //try
                //{
                //    if (jsonValue == null)
                //    {
                //        value = null;
                //    }
                //    else if (IsJsonDateTime(jsonValue.ToString()))
                //    {
                //        value = JsonToDateTime(jsonDateRegex.Match(jsonValue.ToString()).Groups[0].Value);
                //    }
                //    else if (jsonValue.JsonType == JsonType.String)
                //    {
                //        value = jsonValue.ToString().Trim('\"');
                //    }
                //    else
                //    {
                //        value = jsonValue;
                //    }

                    dict.Add(key, FormatJsonValue(jsonValue));
                //}
                //catch (Exception)
                //{
                //    continue;
                //}
            }

            return dict;
        }

        public static IEnumerable<IDictionary> ToDictionaryList(this JsonValue json)
        {
            if (json.JsonType != JsonType.Array)
            {
                return null;
            }
            else
            {
                List<IDictionary> lst = new List<IDictionary>(json.Count);

                foreach (JsonObject jo in json)
                {
                    var dict = ToSingleDictionary(jo);
                    lst.Add(dict);
                }

                return lst;
            }


        }

        public static JsonArray ToJsonArray(this string str)
        {
            return ToJsonArray(str, null);
        }

        public static JsonArray ToJsonArray(this string str, JsonArray defaultValue)
        {
            if (str.IsNullOrEmpty())
            {
                return defaultValue;
            }

            return (JsonArray)JsonArray.Parse(str);
        }

        private static DateTime JsonToDateTime(string jsonDate)
        {
            string value = jsonDate;
            DateTimeKind kind = DateTimeKind.Utc;
            int index = value.IndexOf('+', 1);
            if (index == -1)
                index = value.IndexOf('-', 1);
            if (index != -1)
            {
                kind = DateTimeKind.Local;
                value = value.Substring(0, index);
            }
            long javaScriptTicks = long.Parse(value, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture);
            long InitialJavaScriptDateTicks = (new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).Ticks;
            DateTime utcDateTime = new DateTime((javaScriptTicks * 10000) + InitialJavaScriptDateTicks, DateTimeKind.Utc);
            DateTime dateTime;
            switch (kind)
            {
                case DateTimeKind.Unspecified:
                    dateTime = DateTime.SpecifyKind(utcDateTime.ToLocalTime(), DateTimeKind.Unspecified);
                    break;
                case DateTimeKind.Local:
                    dateTime = utcDateTime.ToLocalTime();
                    break;
                default:
                    dateTime = utcDateTime;
                    break;
            }
            return dateTime;
        }


        #region "jsonArray To DataSource"

        private static readonly Regex PropertNameRegex =new Regex(@"^[A-Za-z]+[A-Za-z0-9_]*$", RegexOptions.Singleline);
        private static readonly Dictionary<string, Type> _typeBySigniture = new Dictionary<string, Type>();

        public static IEnumerable ToDataSource(this JsonArray jsonArray)
        {
            if (jsonArray.Count <= 0)
            {
                return null;
            }

            JsonObject firstDict = null;
            bool hasData = false;

            foreach (JsonObject currentDict in jsonArray)
            {
                hasData = true;
                firstDict = currentDict;
                break;
            }

            if (!hasData)
            {
                return new object[] { };
            }

            if (firstDict == null)
            {
                throw new ArgumentException("IDictionary entry cannot be null");
            }

            string typeSigniture = GetTypeSigniture(firstDict);

            Type objectType = DataSourceCreator.GetTypeByTypeSigniture(typeSigniture);


            if (objectType == null)
            {
                TypeBuilder tb = DataSourceCreator.GetTypeBuilder(jsonArray.GetHashCode());

                ConstructorBuilder constructor =
                            tb.DefineDefaultConstructor(
                                        MethodAttributes.Public |
                                        MethodAttributes.SpecialName |
                                        MethodAttributes.RTSpecialName);

                foreach (var pair in firstDict)
                {
                    if (PropertNameRegex.IsMatch(Convert.ToString(pair.Key), 0))
                    {
                        DataSourceCreator.CreateProperty(tb,
                                       Convert.ToString(pair.Key),
                                       pair.Value == null
                                           ? typeof(object)
                                           : GetJsonValueType(pair.Value));
                    }
                    else
                    {
                        throw new ArgumentException(
                                    @"Each key of IDictionary must be 
                                alphanumeric and start with character.");
                    }
                }
                objectType = tb.CreateType();

                _typeBySigniture.Add(typeSigniture, objectType);
            }
            return GenerateEnumerable(objectType, jsonArray, firstDict);

        }

        private static IEnumerable GenerateEnumerable(Type objectType, JsonValue list, JsonObject firstDict)
        {
            var listType = typeof(List<>).MakeGenericType(new[] { objectType });
            var listOfCustom = Activator.CreateInstance(listType);

            foreach (JsonObject currentDict in list)
            {
                if (currentDict == null)
                {
                    throw new ArgumentException("IDictionary entry cannot be null");
                }
                var row = Activator.CreateInstance(objectType);
                foreach (var pair in firstDict)
                {
                    if (currentDict.ContainsKey(pair.Key))
                    {
                        PropertyInfo property =
                            objectType.GetProperty(Convert.ToString(pair.Key));

                        try
                        {
                            property.SetValue(
                                row,
                                Convert.ChangeType(
                                        currentDict[pair.Key].FormatJsonValue(true, property.PropertyType),
                                        property.PropertyType,
                                        null),
                                null);
                        }
                        catch (Exception)
                        {
                            continue;
                        }


                    }
                }
                listType.GetMethod("Add").Invoke(listOfCustom, new[] { row });
            }
            return listOfCustom as IEnumerable;
        }

        private static string GetTypeSigniture(JsonObject firstDict)
        {
            StringBuilder sb = new StringBuilder();

            foreach (var pair in firstDict)
            {
                sb.AppendFormat("_{0}_{1}", pair.Key, DataSourceCreator.GetValueType(pair.Value));
            }
            return sb.ToString().GetHashCode().ToString().Replace("-", "Minus");
        }

        private static Type GetJsonValueType(JsonValue value)
        {
            if (value == null)
            {
                return typeof(object);
            }
            else if (value.IsJsonDateTime())
            {
                return typeof(DateTime);
            }
            else if (value.JsonType == JsonType.String)
            {
                return typeof(string);
            }
            else
            {
                return typeof(JsonPrimitive);
            }
        }


        #endregion
    }
}
